EntityLiving = class(Entity)

EntityLiving.moveSpeed = 0.08
EntityLiving.jumpHeight = 0.5
EntityLiving.gravity = 0.1
EntityLiving.onGroundSpeedDelay = 0.6

EntityLiving.inLiquidGravity = 0.03
EntityLiving.inLiquidJumpSpeed = 0.1
EntityLiving.inLiquidJumpHeight = 0.3
EntityLiving.inLiquidMoveSpeed = 0.04
EntityLiving.inWaterSpeedDecay = 0.7
EntityLiving.inLavaSpeedDecay = 0.6

function EntityLiving:init()
    Entity.init(self)
    self.fallDist = 0
    self.walkDist,self.walkDistO = 0,0
    self.maxHealth = 20
    self.health = self.maxHealth
end

function EntityLiving:setAI(ai)
    self.AI = ai
end

function EntityLiving:resetPos()
    local d = world.chunkManager.LoadChunkDistance * 16
    local x0,x1,z0,z1 = player.lastPos.x - d,player.lastPos.x + d - 16,player.lastPos.z - d,player.lastPos.z + d - 16
    x0,x1,z0,z1 = math.floor(x0 + 1),math.floor(x1),math.floor(z0 + 1),math.floor(z1)
    if x0 < world.x0 then x0 = world.x0 end
    if z0 < world.x0 then z0 = world.x0 end
    if x1 > world.x1 then x1 = world.x1 end
    if z1 > world.x1 then z1 = world.x1 end
    local x,z
    repeat
        x = math.random(x0,x1)
        z = math.random(z0,z1)
    until world:isBlockLoaded(x,z)
    local y = world.height
    while y > 0 and world:isAirBlock(x,y,z) do
        y = y - 1
    end
    y = y + 3
    self:setPos(x,y,z)
end

function EntityLiving:turn(xo,yo)
    self.yRot = self.yRot + yo
    if self.yRot > 180 then
        self.yRot = self.yRot - 360
    elseif self.yRot < 180 then
        self.yRot = self.yRot + 360
    end
    self.xRot = self.xRot + xo
    if self.xRot > 90 then
        self.xRot = 90
    elseif self.xRot < -90 then
        self.xRot = -90
    end
end

function EntityLiving:tick()
    self.xo = self.x
    self.yo = self.y
    self.zo = self.z
    self.walkDistO = self.walkDist
    
    self.inWater = self:isInWater()
    self.inLava = self:isInLava()
    
    if self.y < -64 then self:fallUnderGround() end
    self:processHealth()
    
    self.AI:AIProcessBeforeMoving()
    
    local xa,za = self.AI:getMoveDirection()
    if xa < -1 then xa = -1 end
    if za < -1 then za = -1 end
    if xa > 1 then xa = 1 end
    if za > 1 then za = 1 end
    
    if self.inWater then
        self:moveRelative(xa,za,self:getMoveSpeed())
        
        self.AI:AIProcessMoving()
        
        self:move(self.xd,self.yd,self.zd)
        self.yd = self.yd - self.inLiquidGravity
        
        self.xd = self.xd * self.inWaterSpeedDecay
        self.yd = self.yd * self.inWaterSpeedDecay * 1.1
        self.zd = self.zd * self.inWaterSpeedDecay
    elseif self.inLava then
        self:moveRelative(xa,za,self:getMoveSpeed())
        
        self.AI:AIProcessMoving()
        
        self:move(self.xd,self.yd,self.zd)
        self.yd = self.yd - self.inLiquidGravity
        
        self.xd = self.xd * self.inLavaSpeedDecay
        self.yd = self.yd * self.inLavaSpeedDecay * 0.8
        self.zd = self.zd * self.inLavaSpeedDecay
    else
        self:moveRelative(xa,za,self:getMoveSpeed())
        
        self.AI:AIProcessMoving()
        
        self:move(self.xd,self.yd,self.zd)
        self.yd = self.yd - self.gravity
        
        if self.onGround then
            self.xd = self.xd * self.onGroundSpeedDelay
            self.zd = self.zd * self.onGroundSpeedDelay
        end
    end
    
    self.xd = self.xd * 0.91
    self.yd = self.yd * 0.98
    self.zd = self.zd * 0.91
    
    self.AI:AIProcessAfterMoving()
end

function EntityLiving:move(xa,ya,za)
    local xaOrg = xa
    local yaOrg = ya
    local zaOrg = za
    
    local xo,zo = self.x,self.z
    
    local aabbs = self:getCollisionAABBs(self.aabb:expend(xa,ya,za))
    
    for k,aabb in pairs(aabbs) do
        ya = aabb:clipYcollide(self.aabb,ya)
    end
    
    self.aabb:move(0,ya,0)
    
    for k,aabb in pairs(aabbs) do
        xa = aabb:clipXcollide(self.aabb,xa)
    end
    self.aabb:move(xa,0,0)
    
    for k,aabb in pairs(aabbs) do
        za = aabb:clipZcollide(self.aabb,za)
    end
    self.aabb:move(0,0,za)
    
    self.horizontalCollision = (xaOrg ~= xa) or (zaOrg ~= za)
    self.verticalCollision = yaOrg ~= ya
    self.onGround = self.verticalCollision and (yaOrg < 0.0)
    
    if self.onGround then
        self:causeFallDamage(self.yd,self.fallDist)
        self.fallDist = 0
    else
        self.fallDist = self.fallDist - self.yd
    end
    
    self.xdo,self.ydo,self.zdo = self.xd,self.yd,self.zd
    
    if xaOrg ~= xa then self.xd = 0.0 end
    if yaOrg ~= ya then self.yd = 0.0 end
    if zaOrg ~= za then self.zd = 0.0 end
    
    self.x = (self.aabb.x0 + self.aabb.x1) / 2.0
    self.y = self.aabb.y0 + self.heightOffset
    self.z = (self.aabb.z0 + self.aabb.z1) / 2.0
    
    self.walkDist = self.walkDist + vec2(self.x - xo,self.z - zo):len() * 0.7
end

function EntityLiving:jumpOnGround()
    self.yd = self.jumpHeight
end

function EntityLiving:jumpInLiquid()
    if self:isFree(self.xd,self.yd + 1.5 --[[- self.y + self.yo]],self.zd) then
        self.yd = self.inLiquidJumpHeight
    else
        self.yd = self.yd + self.inLiquidJumpSpeed
    end
end

function EntityLiving:getCollisionAABBs(movingAABB)
    return world:getCubesWithInAABB(movingAABB)
end

-- 掉落伤害处理
function EntityLiving:causeFallDamage(yd,fallDist)
    if yd < -0.7 then
        self.health = self.health - (fallDist - 2)
    end
end

function EntityLiving:fallUnderGround()
    self.removed = true
end

-- 实体的移动速度决定
function EntityLiving:getMoveSpeed()
    if self.inWater or self.inLava then
        return self.inLiquidMoveSpeed
    else
        if self.onGround == false then
            return self.moveSpeed * 0.2
        else
            return self.moveSpeed
        end
    end
end

-- 实体死亡后的处理，可以重生，也可以撒一些粒子然后移除实体
function EntityLiving:die()
    self.removed = true
end

function EntityLiving:processHealth()
    if self.health <= 0 then
        self:die()
    elseif self.health > self.maxHealth then
        self.health = self.maxHealth
    end
end
